/*
 *  Copyright (C) 2011 glauco
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.balisunrise.swing.builders;

import java.lang.reflect.Field;
import org.balisunrise.annotation.util.AnnotationsUtilitiesException;
import org.balisunrise.annotation.util.FieldUtilities;
import org.balisunrise.annotation.util.NameUtil;
import org.balisunrise.application.Application;
import org.balisunrise.builder.BuilderException;
import org.balisunrise.builder.FactoryException;
import org.balisunrise.common.Criterionable;
import org.balisunrise.common.EntityList;
import org.balisunrise.common.EntityLoader;
import org.balisunrise.common.EntitySelection;
import org.balisunrise.common.FieldComponent;
import org.balisunrise.common.SelectionLoader;
import org.balisunrise.swing.commons.CriterionablePanelField;
import org.balisunrise.swing.commons.EmbeddedPanelField;
import org.balisunrise.swing.commons.PanelField;
import org.balisunrise.swing.commons.SelectionAction;
import org.balisunrise.swing.commons.SelectionPanelField;

/**
 *
 * @author glauco
 */
public class PanelFieldBuilder 
        implements org.balisunrise.builder.PanelFieldBuilder{

    private static final String MESSAGE = "Erro ao construir uma PanelField.\n";

    @Override
    public org.balisunrise.common.PanelField buildPanelField(
            Field field, String alias)
            throws BuilderException {

        if(field == null){
            throw new BuilderException(MESSAGE +
                    "field não pode ser nulo.");
        }

        if(alias == null){
            throw new BuilderException(MESSAGE +
                    "alias não pode ser nulo.");
        }

        try{
            return buildPanelField(FieldUtilities.create(field, alias));
        }catch(AnnotationsUtilitiesException auex){
            throw new BuilderException(MESSAGE +
                    auex.getMessage(), auex.getCause());
        }
    }

    @Override
    public org.balisunrise.common.PanelField buildPanelField(FieldUtilities fu)
            throws BuilderException {
        
        if(fu == null){
            throw new BuilderException(MESSAGE +
                    "fieldUtilities não pode ser nulo.");
        }

        switch(fu.type){
            case FieldUtilities.DEFINITION:
                return buildDefinition(fu);
            case FieldUtilities.EMBEDDED:
                return buildEmbedded(fu);
            case FieldUtilities.SELECTION:
                return buildSelection(fu);
        }

        throw new BuilderException(MESSAGE +
                    "Atributo TYPE do fieldUtilities invalido.");
    }

    private org.balisunrise.common.PanelField buildDefinition(FieldUtilities fu)
            throws BuilderException{
        
        PanelField pf;

        if (fu.selection){
            CriterionablePanelField spf = new CriterionablePanelField();
            spf.setFilterType(fu.selectionFilterType);
            pf = spf;
        }else
            pf = new PanelField();

        pf.setAlias(fu.alias);
        pf.setEntityClass(fu.field.getDeclaringClass());
        pf.setFieldClass(fu.field.getType());
        pf.setFieldName(fu.field.getName());
        pf.setFields(FieldComponentsBuilder.buildPanelFieldComponets(fu));
        pf.setJumpToNextRow(fu.jumpToNextRow);
        pf.setName(NameUtil.buildName(pf));

        for (FieldComponent fc : pf.getFieldComponents()) {
            // atribui owner
            fc.setOwner(pf);
        }
        
        return pf;
    }

    private org.balisunrise.common.PanelField buildEmbedded(FieldUtilities fu)
            throws BuilderException{

        // grupo
        EmbeddedPanelField epf = new EmbeddedPanelField();

        // atribuições
        epf.setAlias(fu.alias);
        epf.setEntityClass(fu.field.getDeclaringClass());
        epf.setFieldClass(fu.field.getType());
        epf.setFieldName(fu.field.getName());
        epf.setName(NameUtil.buildName(epf));

        // para cada campo emcapsulado
        for (FieldUtilities efu : fu.embedded) {
            // cria novos campos
            org.balisunrise.common.PanelField newPF = buildPanelField(efu);
            // atribui owner
            newPF.setOwner(epf);
            // se for um critionable adiciona o preName
            if(newPF instanceof Criterionable)
                ((Criterionable)newPF).addPrefix(fu.field.getName());

            // adiciona o campo
            epf.getLayouts().add(newPF);
        }

        return epf;
    }

    private org.balisunrise.common.PanelField buildSelection(FieldUtilities fu)
            throws BuilderException{

        SelectionPanelField spf = new SelectionPanelField();

        // atribuições
        spf.setAlias(fu.alias);
        spf.setEntityClass(fu.field.getDeclaringClass());
        spf.setFieldClass(fu.field.getType());
        spf.setFieldName(fu.field.getName());
        spf.setName(NameUtil.buildName(spf));
        spf.setEmptyForAll(false);
        spf.setFilterType(fu.selectionFilterType);

        // para cada campo emcapsulado
        for (FieldUtilities efu : fu.embedded) {

            // cria novos campos
            org.balisunrise.common.PanelField newPF = buildPanelField(efu);
            newPF.setOwner(spf);

            // desabilita o campo se não for seleção.
            newPF.setEnabled(false);

            // adiciona o campo
            spf.addFieldLayout(newPF);
        }

        // Selection Action
        SelectionAction sa = FieldComponentsBuilder.buildSelectionAction(fu);
        sa.setOwner(spf);
        spf.setSelectionAction(sa);

        // Sentity Selection
        EntitySelection es = Application.get()
                .getVisualEntityManager()
                .getEntitySelectionBuilder()
                .create(fu.field.getType(), fu.search);

        // Entity List
        EntityList entityList = Application.get()
                .getFactory()
                .createEntityList();

        // Entity Loader
        EntityLoader el;
        try{
            el = Application.get()
                    .getFactory()
                    .createEntityLoader(spf.getEntityClass(), entityList);
        }catch(FactoryException ex){
            throw new BuilderException(MESSAGE + ex.getMessage(), ex);
        }

        // Selection Loader
        SelectionLoader sl;
        try{
            sl = Application.get()
                    .getFactory()
                    .createSelectionLoader(spf, es);
            sl.setNotFoundMessage(fu.notFoundMessage);
        }catch(FactoryException ex){
            throw new BuilderException(MESSAGE + ex.getMessage(), ex);
        }
        
        // atribui pai
        entityList.setOwner(spf);
        es.setOwner(spf);
        el.setOwner(spf);
        sl.setOwner(spf);

        // atribuições ao SelectionLoader
        sl.setEntityList(entityList);
        sl.setEntityLoader(el);

        // atribuições ao EntityGrid do EntitySelection
        es.getEntityGrid().setEntityLoader(el);
        
        // atribui ao dono
        spf.setEntitySelection(es);
        spf.setSelectionLoader(sl);
        
        return spf;
    }

}
